#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <pthread.h>
#include "configfile.h"
#include "server.h"

/* We should have the servers as follows
   ideally
   MASTER NODE
   a) an SSL connection to listen to clients
      Master node adds/removes users
      establishes new sessions  
      (with a symmetric session key)
   b) a plain text connection for internal 
      communication between nodes

   But we will have plain connections only
   as our focus is on filegroup handling
   between client-edge-core.

   EDGE NODE
   a) a plain text connection, for clients. 
      All communications should be through
      symmetric session key.
   b) a plain text connection for internal 
      communication between edge and core
   c) a plain text connection for internal 
      communication between edge and master
      Note: We shall be having two connect
      ions for fault tolerance, even if 
      edge-core breaks edge-master should
      not break.  

   EDGE NODE
   a) a plain text connection, for clients. 
      All communications should be through
      symmetric session key.
   b) a plain text connection for internal 
      communication between nodes

   CORE NODE
   a) a plain text connection for internal 
      communication between edge and core
   b) a plain text connection for internal 
      communication between edge and master
      Note: We shall be having two connect
      ions for fault tolerance, even if 
      edge-core breaks edge-master should
      not break.  
*/

/* Note : For the scope of this projet
   all connections are plain text
    The focus is to demonstrate
    filegrouping and storing the file in
    corenode and fetching via edge node
 */ 
  
#define DEBUG 0

#if DEBUG
#define DEBUG_LOG(fmt , args...) \
    printf("\n[%s][%d] " fmt "\n" , __FUNCTION__, __LINE__ , ##args );
#else
#define DEBUG_LOG(fmt , args... ) 
#endif



fd_set active_fd_set; 
fd_set read_fd_set;


int make_socket (unsigned short port);
int prepare_socket_connection(void);

int make_socket (unsigned short port)
{
  int sock;
  struct sockaddr_in name;

  /* Create the socket. */
  sock = socket (AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
  {
      fprintf (stderr, "socket error %s", strerror(errno));
      exit (EXIT_FAILURE);
  }

  /* Give the socket a name. */
  name.sin_family = AF_INET;
  name.sin_port = htons (port);
  name.sin_addr.s_addr = htonl (INADDR_ANY);
  if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
  {
      fprintf (stderr, "bind error %s", strerror(errno));
      exit (EXIT_FAILURE);
  }

  return sock;
}

int prepare_socket_connection(void)
{
    int sock;
    int backlog=5;
    
    sock = make_socket(cfile->my_port);

    if (listen (sock, backlog) < 0)
    {
      fprintf (stderr, "listen error %s", strerror(errno));
      exit (EXIT_FAILURE);
    }
    
    FD_ZERO (&active_fd_set);
    FD_SET (sock, &active_fd_set);
    
    return sock;

}


int
receive_message (int filedes,config_file_t *cfile)
{
  char *buffer = NULL;
 
  int nbytes;
  int readCount = 0;
  
  buffer = malloc(MAXMSG);
  if (!buffer) return -1;
  
  //do {
      
      nbytes = read (filedes, buffer+readCount, (MAXMSG-readCount));
      if (nbytes < 0)
      {
          /* Read error. */
          fprintf (stderr, "listen error %s", strerror(errno));
          return -1;
      }
      else if (nbytes)
      {
          /* Data read. */
          readCount+=nbytes;
          fprintf (stderr, "Server: got message: nbytes=%d\n",
              readCount);
      }
  //} while(nbytes);

  DEBUG_LOG("About to handle the message %p size %d by node_type  %d \n",
            buffer, readCount, cfile->my_node_type);
  if (readCount) {
  switch(cfile->my_node_type)
  {
  case CONFIG_MASTER:
      master_msg_send((unsigned char *)buffer, readCount,filedes);
      break;
  case CONFIG_EDGE:
      edge_msg_send((unsigned char *)buffer, readCount,filedes);
      break;
  case CONFIG_CORE:
      core_msg_send((unsigned char *)buffer, readCount,filedes);
      break;
  }
  } else {
      free(buffer);
      return -1;
  }
  free(buffer);
  return 0;
}



int listen_in_loop(int sock,config_file_t *cfile )
{
   int i;
   struct sockaddr_in clientname; 
   while (1)
   {
      /* Block until input arrives on one or more active sockets. */
      read_fd_set = active_fd_set;
      //DEBUG_LOG("Server: Before select %d.\n", sock);      
      if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
        {
          fprintf (stderr, "select error %s", strerror(errno));
          exit (EXIT_FAILURE);
        }

      /* Service all the sockets with input pending. */
          
      for (i = 0; i < FD_SETSIZE; ++i)
        if (FD_ISSET (i, &read_fd_set))
          {
            //DEBUG_LOG("Server: Got a connection on %d.\n", i);
            if (i == sock)
            {
              /* Connection request on original socket. */
              int new;
              socklen_t size;
              size = sizeof (clientname);

              //DEBUG_LOG("Server: waiting for connections on %d.\n" ,
               //   sock);
              
              new = accept (sock,
                            (struct sockaddr *) &clientname,
                            &size);
              if (new < 0)
              {
                  fprintf (stderr, "accept error %s",
                            strerror(errno));
                  exit (EXIT_FAILURE);
              }
              DEBUG_LOG("Server: connect from host %s, port %hd.\n",
                       inet_ntoa (clientname.sin_addr),
                       ntohs (clientname.sin_port));
              FD_SET (new, &active_fd_set);
            }
            else
            {
              /* Data arriving on an already-connected socket. */
              if (receive_message (i, cfile) < 0)
              {
                  close (i);
                  FD_CLR (i, &active_fd_set);
              }
            }
          }
    }
    return 0;
}

int terminate_socket_connection(int sock)
{
     close(sock);
    return 0;     
}
void *
start_server (void *arg);
int start_listening(config_file_t *cfile,pthread_t  *thread_id)
{

    int retval;
    
    retval = pthread_create( thread_id,
		     NULL,
		     start_server,
		     (void*)cfile);
	if (!retval) return 1; else
    return 0;     
		     
    //fprintf(stdmaster_msg_senderr,"%s Server running on main", __FUNCTION__);
    //start_server(cfile);
    //return 1;
    
}

void *
start_server (void *arg)
{
    int sock;   

    if (!arg) {
    	fprintf(stderr,"NULL arg to %s. Exiting", __FUNCTION__);
	    pthread_exit(NULL);
    }

    sock = prepare_socket_connection();
    fprintf(stderr,"%s: socket ready at %d", __FUNCTION__, sock);
    listen_in_loop(sock, (config_file_t *) arg);
    terminate_socket_connection(sock);
    pthread_exit(NULL);
}

 

